/*  
 *  Vector3D.h
 *
 *  Created by Pete Willemsen on 01/19/2011.
 *  Copyright 2011 Department of Computer Science, University of Minnesota Duluth. All rights reserved.
 *
 * This file is part of CS5721 Computer Graphics library (cs5721Graphics).
 *
 * cs5721Graphics is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * cs5721Graphics is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with cs5721Graphics.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifndef __CS5721_GRAPHICSLIB_VECTOR3__
#define __CS5721_GRAPHICSLIB_VECTOR3__ 1

#include <cassert>
#include <iostream>
#include <cmath>

//! Namespace to hold all classes and functionality for CS 5721.
/** 
 * Namespace will be set to contain classes and functionality related
 * to the projects in CS 5721 Computer Graphics at the University of
 * Minnesota Duluth.
 */
namespace cs5721 {
    //! Class representing a basic 3D vector for use with ray tracers and rasterizers in CS5721

    /**
     *
     */
    class Vector3D {
    public:

        //! Default constructor for the Vector3D class.  Sets all values to 0.

        Vector3D() {
            data[0] = data[1] = data[2] = 0;
        }

        //! Constructor for the Vector3D class that sets values to provided x, y, and z values.

        Vector3D(const double x, const double y, const double z) {
            data[0] = x;
            data[1] = y;
            data[2] = z;
        }

        //! Destructor

        ~Vector3D() {
        }

        //!

        /**
         */
        const double operator[](const int i) const {
            // do a sanity check to make sure indices are OK!
            assert(i >= 0 && i < 3);
            return data[i];
        }

        //!

        /**
         */
        void set(const double x, const double y, const double z) {
            data[0] = x;
            data[1] = y;
            data[2] = z;
        }

        void clamp();

        //! Destructively normalize the vector, making it unit length.
        /** @return the length of the vector prior to normalization.
         */
        double normalize(void);

        //! Compute the dot product between two vectors.

        /**
         */
        double dot(const Vector3D &v) const {
            return data[0] * v.data[0] + data[1] * v.data[1] + data[2] * v.data[2];
        }

        //! Compute the cross product between two vectors.

        /**
         */
        Vector3D cross(const Vector3D &v) const {
            Vector3D vector;
            vector.data[0] = data[1] * v.data[2] - data[2] * v.data[1];
            vector.data[1] = data[2] * v.data[0] - data[0] * v.data[2];
            vector.data[2] = data[0] * v.data[1] - data[1] * v.data[0];
            return vector;
        }

        //! Vector3D assignment operator.  Let's you do v1 = v2;

        /** @param
         * @return a reference to the Vector3D to allow chaining of operations.
         */
        Vector3D & operator=(const Vector3D &rhs) {
            // v1 = v2 --> same as v1.operator=(v2);
            data[0] = rhs.data[0];
            data[1] = rhs.data[1];
            data[2] = rhs.data[2];
            return *this;
        }

        //!

        Vector3D & operator+=(const Vector3D &rhs) {
            data[0] += rhs.data[0];
            data[1] += rhs.data[1];
            data[2] += rhs.data[2];
            return *this;
        }

        Vector3D & operator-=(const Vector3D &rhs) {
            data[0] -= rhs.data[0];
            data[1] -= rhs.data[1];
            data[2] -= rhs.data[2];
            return *this;
        }

        Vector3D & operator*=(const double c) {
            data[0] *= c;
            data[1] *= c;
            data[2] *= c;
            return *this;
        }

        Vector3D & operator/=(const double c) {
            data[0] /= c;
            data[1] /= c;
            data[2] /= c;
            return *this;
        }

    private:
        friend std::ostream & operator<<(std::ostream& os, const Vector3D &v);
        friend std::istream & operator>>(std::istream& is, Vector3D &v);

        double data[3];
    };

    const Vector3D operator+(const Vector3D& lhs, const Vector3D& rhs);
    const Vector3D operator-(const Vector3D& lhs, const Vector3D& rhs);
    const Vector3D operator*(const Vector3D& lhs, const double c);
    const Vector3D operator/(const Vector3D& lhs, const double c);


}

#endif // __CS5721_GRAPHICSLIB_VECTOR3__
